/******************************************************************************
/// @copyright Copyright (c) 2017 <dji-innovations, Corp. RM Dept.>
/// @license MIT License
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction,including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense,and/or sell
/// copies of the Software, and to permit persons to whom the Software is furnished
/// to do so,subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
/// THE SOFTWARE.
*******************************************************************************/
/**
  * @update 2016年5月24日22:50:28
  * @note  更新了串口接收的方式。
        *				//使用DMA来接收，每当空闲中断的时候，也就是不收数据了的时候处理接收到的数据。
        *				//DMA中断正常情况下是不触发的，
  *要保证MAX-RX-SIZE大于每次的一帧数据长度
        *				//所以DMA的作用就是加快收数据的速率，然后其他没了
        *				//################### 状态机：
  *########################
        *				进入空闲中断--处理收到的数据--清除中断标记
        *				//--重新初始化DMA--等待接收数据直到空闲--进入空闲中断
  *       //################### 状态机： ########################
  * @after	测试过一次，成功！ 后面可能还要测一下
  */

#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "py/runtime.h"
#include "py/stream.h"
#include "py/mperrno.h"
#include "py/mphal.h"

#include "uart_idle_dma.h"
#include "log.h"
// #include "usart.h"

#define MAX_DMA_COUNT 1024
#define DBUS_RX_MAX_BUFLEN 30
#define BT_RX_MAX_BUFLEN 100 //这个只要比一帧大就行拉
#define MIDI_RX_BUFF_LEN 10

#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])))

uint8_t rc_buffer[BT_RX_MAX_BUFLEN];

uint8_t u7_buffer[64];
UART_HandleTypeDef huart7;
DMA_HandleTypeDef hdma_uart7_rx;

typedef struct
{
    UART_HandleTypeDef *huart;
    uart_rx_idle_handle_func callback;
} uart_rx_callback_t;
static uart_rx_callback_t uart_rx_idle_callback[5];

void uart_idle_handle_register(UART_HandleTypeDef *huart, uart_rx_idle_handle_func callback)
{
    for (int i = 0; i < ARRAY_SIZE(uart_rx_idle_callback); ++i)
    {
        if (uart_rx_idle_callback[i].huart == NULL)
        {
            uart_rx_idle_callback[i].huart = huart;
            uart_rx_idle_callback[i].callback = callback;
            break;
        }
    }
}

/**
        *@bref use uart idle it + dma(no it) to receive a frame data.
        */
void uart_idle_dma_rx_init(UART_HandleTypeDef *huart, uint8_t *rx_buff, uint16_t buff_len)
{
//    __HAL_UART_CLEAR_IDLEFLAG(huart);
#ifdef STM32F1
    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_IDLE);
    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
#elif defined STM32G4
    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_IDLE);
    __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
#elif defined STM32F4
    __HAL_UART_CLEAR_OREFLAG(huart);
    __HAL_UART_CLEAR_IDLEFLAG(huart);
#else
#error "fixme"
#endif
    __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
    HAL_UART_Receive_DMA(huart, rx_buff, buff_len);
}

static void uart_reset_idle_rx_callback(UART_HandleTypeDef *huart)
{
    if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))
    {
        __HAL_UART_CLEAR_IDLEFLAG(huart);
        // clear idle it flag
        //重启DMA
        uint32_t DMA_FLAGS = __HAL_DMA_GET_TC_FLAG_INDEX(
            huart->hdmarx); //根据串口的不同来选择清除不同的DMA标志位

        __HAL_DMA_DISABLE(huart->hdmarx);
        __HAL_DMA_CLEAR_FLAG(huart->hdmarx, DMA_FLAGS);
        __HAL_DMA_SET_COUNTER(huart->hdmarx, MAX_DMA_COUNT);
        // huart->hdmarx->Instance->NDTR = MAX_DMA_COUNT;

        __HAL_DMA_ENABLE(huart->hdmarx);
    }
}

void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (uartHandle->Instance == UART7)
    {
        /* UART7 clock enable */
        __HAL_RCC_UART7_CLK_ENABLE();
        __HAL_RCC_DMA1_CLK_ENABLE();
        __HAL_RCC_DMA2_CLK_ENABLE();
        __HAL_RCC_GPIOE_CLK_ENABLE();
        /**UART7 GPIO Configuration    
        PE8     ------> UART7_TX
        PE7     ------> UART7_RX 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF8_UART7;
        HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

        /* UART7 DMA Init */
        /* UART7_RX Init */
        hdma_uart7_rx.Instance = DMA1_Stream3;
        hdma_uart7_rx.Init.Channel = DMA_CHANNEL_5;
        hdma_uart7_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_uart7_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_uart7_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_uart7_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_uart7_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_uart7_rx.Init.Mode = DMA_NORMAL;
        hdma_uart7_rx.Init.Priority = DMA_PRIORITY_LOW;
        hdma_uart7_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_uart7_rx) != HAL_OK)
        {

        }

        __HAL_LINKDMA(uartHandle, hdmarx, hdma_uart7_rx);

        /* UART7 interrupt Init */
        HAL_NVIC_SetPriority(UART7_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(UART7_IRQn);
    }
}

rc_t rc2;

void parse_rc(rc_t* p_rc, uint8_t *buf, uint16_t len)
{
    if (len < 10 || buf[0] != 0xff || buf[1] != 0x5a) {
        return ;
    }
    uint8_t sum = 0;
    for (int i=2; i<=8; ++i) {
        sum += buf[i];
    }
    if (sum != buf[9]) {
        return ;    //checksum failed
        //TODO: when failed many times. we need reset uart.
    }

    memcpy(p_rc, &buf[2], sizeof(rc_t));
    static int logcnt;
    if (logcnt++ >= 5) 
    {
        logcnt = 0;
        LOGI("ch [%3d %3d %3d %3d]", p_rc->lx, p_rc->ly, p_rc->rx, p_rc->ry);
    }
    // ESP_LOGI(TAG, "dir[%d %d %d %d]", rc.UP, rc.DOWN, rc.LEFT, rc.RIGHT);
}

static uint8_t log_buffer[64];
void u7_idle_cb(uint8_t *buf, uint16_t size) {
    buf[size-1] = 0;
    // LOGI("uart idle u7 %d %s", size, buf);
    LOGI("u7:%d %s", size, log_hex_dump_line(buf, 10, log_buffer));
    parse_rc(&rc2, buf, size);
}

/* UART7 init function */
void uart7_idle_init(void)
{
    huart7.Instance = UART7;
    huart7.Init.BaudRate = 115200;
    huart7.Init.WordLength = UART_WORDLENGTH_8B;
    huart7.Init.StopBits = UART_STOPBITS_1;
    huart7.Init.Parity = UART_PARITY_NONE;
    huart7.Init.Mode = UART_MODE_TX_RX;
    huart7.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart7.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart7);

    uart_idle_handle_register(&huart7, u7_idle_cb);
    uart_idle_dma_rx_init(&huart7, u7_buffer, sizeof(u7_buffer));
}

// void dma_rc_dbus_decode_callback(rc_info_t *rc, uint8_t *buff);
//这个函数放在BT对应的 Usart-IRQHandler 里面
void langgo_uart_dma_idle_it_callback(UART_HandleTypeDef *huart)
{
    // confirm uart idle it set!
    for (int i = 0; i < ARRAY_SIZE(uart_rx_idle_callback); ++i)
    {
        if (NULL != uart_rx_idle_callback[i].callback && huart == uart_rx_idle_callback[i].huart)
        {
            uint16_t rx_len = MAX_DMA_COUNT - __HAL_DMA_GET_COUNTER(huart->hdmarx);

            uart_rx_idle_callback[i].callback(huart->pRxBuffPtr, rx_len);
        }
        else
        {
            break;
        }
    }

    uart_reset_idle_rx_callback(huart);
}


// Include MicroPython API.
#include "py/runtime.h"

//添加import rc 相关支持
// This is the function which will be called from Python as rc.
STATIC mp_obj_t rc_channels() {
    // Extract the ints from the micropython input objects.

    mp_obj_t tuple[] = {
        MP_OBJ_NEW_SMALL_INT(rc2.lx),
        MP_OBJ_NEW_SMALL_INT(rc2.ly),
        MP_OBJ_NEW_SMALL_INT(rc2.rx),
        MP_OBJ_NEW_SMALL_INT(rc2.ry),
    };

    // convert to MicroPython object.
    return mp_obj_new_tuple(4, tuple);
}

STATIC mp_obj_t rc_keys() {
    mp_obj_t tuple[] = {
        MP_OBJ_NEW_SMALL_INT(rc2.R1),
        MP_OBJ_NEW_SMALL_INT(rc2.R2),
        MP_OBJ_NEW_SMALL_INT(rc2.R3),
        MP_OBJ_NEW_SMALL_INT(rc2.L1),
        MP_OBJ_NEW_SMALL_INT(rc2.L3),
        MP_OBJ_NEW_SMALL_INT(rc2.BT),
        MP_OBJ_NEW_SMALL_INT(rc2.A),
        MP_OBJ_NEW_SMALL_INT(rc2.B),
        MP_OBJ_NEW_SMALL_INT(rc2.X),
        MP_OBJ_NEW_SMALL_INT(rc2.Y),
        MP_OBJ_NEW_SMALL_INT(rc2.POWER),
        MP_OBJ_NEW_SMALL_INT(rc2.START),
    };

    // convert to MicroPython object.
    return mp_obj_new_tuple(12, tuple);
}

STATIC mp_obj_t rc_dirs() {
    mp_obj_t tuple[] = {
        MP_OBJ_NEW_SMALL_INT(rc2.UP),
        MP_OBJ_NEW_SMALL_INT(rc2.DOWN),
        MP_OBJ_NEW_SMALL_INT(rc2.LEFT),
        MP_OBJ_NEW_SMALL_INT(rc2.RIGHT),
    };

    // convert to MicroPython object.
    return mp_obj_new_tuple(4, tuple);
}

STATIC mp_obj_t rc_A() {
    return MP_OBJ_NEW_SMALL_INT(rc2.A);
}

STATIC mp_obj_t rc_B() {
    return MP_OBJ_NEW_SMALL_INT(rc2.B);
}

// Define a Python reference to the function above.
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_channels_obj, rc_channels);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_keys_obj, rc_keys);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_dirs_obj, rc_dirs);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_A_obj, rc_A);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(rc_B_obj, rc_B);

// Define all properties of the module.
// Table entries are key/value pairs of the attribute name (a string)
// and the MicroPython object reference.
// All identifiers and strings are written as MP_QSTR_xxx and will be
// optimized to word-sized integers by the build system (interned strings).
STATIC const mp_rom_map_elem_t rc_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rc) },
    { MP_ROM_QSTR(MP_QSTR_channels), MP_ROM_PTR(&rc_channels_obj) },
    { MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&rc_keys_obj) },
    { MP_ROM_QSTR(MP_QSTR_dirs), MP_ROM_PTR(&rc_dirs_obj) },
    { MP_ROM_QSTR(MP_QSTR_A), MP_ROM_PTR(&rc_A_obj) },
    { MP_ROM_QSTR(MP_QSTR_B), MP_ROM_PTR(&rc_B_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rc_module_globals, rc_module_globals_table);

// Define module object.
const mp_obj_module_t rc_user_cmodule = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&rc_module_globals,
};

// Register the module to make it available in Python.
// Note: the "1" in the third argument means this module is always enabled.
// This "1" can be optionally replaced with a macro like MODULE_CEXAMPLE_ENABLED
// which can then be used to conditionally enable this module.
MP_REGISTER_MODULE(MP_QSTR_rc, rc_user_cmodule, 1);